/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { parseJsonText, PathUtil } from '@ohos/hvigor';
import * as fse from 'fs-extra';
import * as path from 'path';
import { basename } from 'path';

import { BuildArtifactConst, BuildDirConst } from '../../src/const/build-directory-const';
import { CommonConst } from '../../src/const/common-const';
import { CompileModeEnum } from '../../src/enum/compile-mode-enum';
import { ModuleType } from '../../src/enum/module-type-enum';
import { ModuleBuildProfile } from '../../src/options/build/module-build-profile';
import { ApiType } from '../../src/project/data/hap-extra-info';
import { InjectUtil } from '../../src/utils/inject-util';

import { initByStageEmptyApp, initByStageEmptyService, initExecuteCommand, runHvigor } from './common/common';
import { ModuleBuildProfileModifier } from './common/modifier/module-build-profile-modifier';
import ModuleTargetBuildOpt = ModuleBuildProfile.ModuleTargetBuildOpt;
import { getJson5Obj } from '../../src/utils/json-util';

const OHOS_PROJECT_KEY = 'ohos-project';
const SELECT_PRODUCT_NAME = 'SELECT_PRODUCT_NAME';
const MODULE_BUILD_DIR = 'MODULE_BUILD_DIR';
const BUNDLE_NAME = 'BUNDLE_NAME';
const BUILD_PATH = 'BUILD_PATH';
const OUTPUT_PATH = 'OUTPUT_PATH';

const OHOS_MODULE_KEY = 'ohos-module';
const SELECT_TARGET = 'SELECT_TARGET';
const TARGETS = 'TARGETS';
const SOURCE_ROOT = 'SOURCE_ROOT';
const RESOURCES_PATH = 'RESOURCES_PATH';

const JS_ASSETS_PATH = 'JS_ASSETS_PATH';
const RES_PATH = 'RES_PATH';
const RES_PROFILE_PATH = 'RES_PROFILE_PATH';
const WORKER_LOADER = 'WORKER_LOADER';
const MANIFEST_JSON = 'MANIFEST_JSON';


describe('test Sync Stage', () => {
  test.concurrent('stage app sync', async () => {
    await initByStageEmptyApp(testSync);
  });

  test.concurrent('stage service sync', async () => {
    await initByStageEmptyService(testSync);
  });
});

describe('test Sync content is right', () => {

  test.concurrent('test stage ArkTS/js super visual path', async () => {
    await initByStageEmptyApp(async (projectDir: string) => {
      const commands = initExecuteCommand({
        sync: true,
        tasks: 'sync'
      });

      // 执行sync
      const result = runHvigor(projectDir, commands);
      expect(result.exitCode).toBe(0);
      let output: string = readSyncOutput();

      expect(output).toContain(
        'entry\\\\build\\\\default\\\\cache\\\\default\\\\default@CompileArkTS\\\\esmodule');
      expect(output).toContain(
        'entry\\\\build\\\\default\\\\cache\\\\default\\\\default@CompileJS\\\\jsbundle');

      new ModuleBuildProfileModifier(projectDir, 'entry').setCompileMode(CompileModeEnum.JS_BUNDLE)
        .outputSync();

      // 再次执行sync
      const jsBundleResult = runHvigor(projectDir, commands);
      expect(jsBundleResult.exitCode).toBe(0);
      output = readSyncOutput();

      // 默认使用模块化编译后，应该没有变更
      expect(output).toContain(
        'entry\\\\build\\\\default\\\\cache\\\\default\\\\default@CompileArkTS\\\\esmodule');
      expect(output).toContain(
        'entry\\\\build\\\\default\\\\cache\\\\default\\\\default@CompileJS\\\\jsbundle');
    });
  });


});

export async function testSync(projectDir: string) {
  const productName = 'default';
  const result = syncProject(productName, projectDir);
  expect(result.exitCode).toBe(0);
  let output: string = readSyncOutput();
  const syncObj = JSON.parse(output);
  expect(Object.keys(syncObj).length).toEqual(8);
  testData(syncObj, productName, projectDir);
  fse.removeSync(path.resolve(projectDir, 'entry', 'src', 'ohosTest'));
  const buildConfigPath = path.resolve(projectDir, 'entry', CommonConst.PROFILE_JSON5);

  const moduleBuildOpt: ModuleBuildProfile.ModuleBuildOpt = getJson5Obj(buildConfigPath);

  moduleBuildOpt.targets = (moduleBuildOpt.targets as ModuleTargetBuildOpt[])
    .filter((target) => target.name !== 'ohosTest');
  fse.outputJSONSync(buildConfigPath, moduleBuildOpt);

  const result2 = syncProject(productName, projectDir);
  expect(result2.exitCode).toBe(0);
  output = readSyncOutput();
  const syncObj2 = JSON.parse(output);
  expect(Object.keys(syncObj2).length).toEqual(8);
  testData(syncObj2, productName, projectDir);
}

export function testData(syncObj: any, productName: string, projectDir: string) {
  // 测试ohos-project对象属性和值
  testProjectAttributes(syncObj, productName, projectDir);

  // 测试模块对象属性和值
  Object.keys(syncObj).forEach((moduleKey) => {
    if (moduleKey.indexOf(OHOS_MODULE_KEY) === -1) {
      return;
    }
    const moduleName = moduleKey.substring(OHOS_MODULE_KEY.length + 1);

    const modulePath = path.resolve(projectDir, moduleName);

    const moduleBuildProfilePath = path.resolve(modulePath, 'build-profile.json5');
    const moduleBuildOpt = parseJsonText(
      fse.readFileSync(path.resolve(moduleBuildProfilePath), { encoding: 'utf-8' }));

    const configOpt = getConfigOpt(projectDir, moduleName, moduleBuildOpt);

    const moduleBuildPath = path.resolve(InjectUtil.getBuildCacheParentDir(modulePath, basename(projectDir)), BuildDirConst.BUILD_ROOT);
    const moduleProductPath = path.resolve(moduleBuildPath, productName);
    const moduleBuildIntermediates = path.resolve(moduleProductPath, BuildDirConst.INTERMEDIATES);

    const moduleJsonObject = syncObj[moduleKey];
    const selectTargetName = 'default';
    expect(moduleJsonObject[SELECT_TARGET]).toEqual(selectTargetName);

    expect(moduleJsonObject[MODULE_BUILD_DIR])
      .toEqual(moduleBuildPath);


    if (moduleName.includes('library')) {
      expect(Object.keys(moduleJsonObject[TARGETS]).length).toEqual(1);
    } else {
      if (fse.existsSync(path.resolve(projectDir, 'entry', 'src', 'ohosTest'))) {
        expect(Object.keys(moduleJsonObject[TARGETS]).length).toEqual(2);
        expect(moduleJsonObject[TARGETS]['ohosTest'] !== undefined).toEqual(true);
      } else if (moduleName.includes('entry')) {
        expect(Object.keys(moduleJsonObject[TARGETS]).length).toEqual(1);
        expect(moduleJsonObject[TARGETS]['ohosTest'] !== undefined).toEqual(false);
      }
    }
    expect(moduleJsonObject[TARGETS]['default'] !== undefined).toEqual(true);

    Object.keys(moduleJsonObject[TARGETS]).forEach((targetName) => {
      const target = moduleJsonObject[TARGETS][targetName];

      const sourceRoot = path.resolve(projectDir, moduleName, 'src',
        targetName === 'ohosTest' ? 'ohosTest' : 'main');
      expect(target[SOURCE_ROOT]).toEqual(sourceRoot);

      expect(target[RESOURCES_PATH].length).toEqual(1);
      expect(target[RESOURCES_PATH][0]).toEqual(path.resolve(sourceRoot, 'resources'));

      const buildPathObj = target[BUILD_PATH];
      expect(Object.keys(buildPathObj).length).toEqual(11);

      const moduleBuildOutputPath = path.resolve(moduleProductPath, BuildDirConst.BUILD_OUTPUTS, targetName);
      expect(buildPathObj[OUTPUT_PATH]).toEqual(moduleBuildOutputPath);

      const intermediatesAssetsPath = path.resolve(moduleBuildIntermediates, BuildDirConst.LOADER_OUT, targetName);
      expect(buildPathObj[JS_ASSETS_PATH]).toEqual(intermediatesAssetsPath);

      const intermediatesRes = path.resolve(moduleBuildIntermediates, BuildDirConst.INTERMEDIATES_RES, targetName);
      const resPath = path.resolve(intermediatesRes, getFirstEntryName(moduleBuildOpt, configOpt));
      expect(buildPathObj[RES_PATH]).toEqual(resPath);

      const resProfilePath = path.resolve(intermediatesRes, BuildDirConst.RESTOOL_BUILD_RESOURCES,
        BuildDirConst.BASE,
        BuildDirConst.PROFILE);
      expect(buildPathObj[RES_PROFILE_PATH]).toEqual(resProfilePath);

      const intermediatesLoaderPath = path.resolve(moduleBuildIntermediates, BuildDirConst.LOADER, targetName);
      const workerLoader = path.resolve(intermediatesLoaderPath, BuildArtifactConst.LOADER_JSON);
      expect(buildPathObj[WORKER_LOADER]).toEqual(workerLoader);

      const intermediatesLegacyManifestJson = path.resolve(moduleBuildIntermediates,
        BuildDirConst.LEGACY_MANIFEST, targetName);
      expect(buildPathObj[MANIFEST_JSON]).toEqual(intermediatesLegacyManifestJson);
    });
  });
}

export function getConfigOpt(projectDir: string, moduleName: string, moduleBuildOpt: any) {
  const jsonFileName = moduleBuildOpt.apiType === ApiType.FA ? 'config.json' : 'module.json5';
  const configJsonPath = path.resolve(projectDir, moduleName, 'src', 'main', jsonFileName);
  return parseJsonText(
    fse.readFileSync(path.resolve(configJsonPath), { encoding: 'utf-8' }));
}

export function getFirstEntryName(moduleBuildOpt: any, configOpt: any): string {
  if (moduleBuildOpt.apiType !== ApiType.FA || configOpt.module.distro.moduleType !== ModuleType.Feature) {
    return '';
  }
  return moduleBuildOpt.entryModules[0];
}

export function testProjectAttributes(syncObj: any, productName: string, projectDir: string) {
  const projectJsonObject = syncObj[OHOS_PROJECT_KEY];
  expect(projectJsonObject[SELECT_PRODUCT_NAME]).toEqual(productName);
  expect(path.resolve(projectJsonObject[MODULE_BUILD_DIR]))
    .toEqual(path.resolve(projectDir, BuildDirConst.BUILD_ROOT));
  expect(projectJsonObject[BUNDLE_NAME]).toContain('com.example.myapplication');
  expect(projectJsonObject[BUILD_PATH][OUTPUT_PATH])
    .toEqual(path.resolve(projectDir, BuildDirConst.BUILD_ROOT, BuildDirConst.BUILD_OUTPUTS, productName));
}

export function readSyncOutput() {
  const syncOutputPath = path.resolve(PathUtil.getHvigorCacheDir(), 'outputs', 'sync', 'output.json');
  expect(fse.existsSync(syncOutputPath)).toBe(true);
  return fse.readFileSync(syncOutputPath, { encoding: 'utf-8' });
}

export function syncProject(productName: string, projectDir: string) {
  const commands = initExecuteCommand({
    sync: true,
    product: productName,
    tasks: 'sync'
  });

  // 执行sync
  const result = runHvigor(projectDir, commands);

  // 判断sync是否执行成功
  expect(result.exitCode).toEqual(0);
  return result;
}
